﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml;

namespace editor.cfg
{
    public class Config
    {
        public Defines Defines { get; private set; }
        public string Namespace { get; private set; }
        public string SimpleName { get; private set; }
        public TBean ValueType { get; private set; }

        public TType KeyType { get; private set; }

        public string CsvDir { get; private set; }

        public string Title { get; private set; }


        private string _index;
        public string Index { get { return _index; } set { _index = value; } }
        private readonly Bean _bean;


        public int SelectedIndex { get; set; }

        public bool IsEditorConfig { get; private set; }

        private readonly string[] _inputs;

        public string DataDir { get { return Path.Combine(CsvDir, _inputs[0]); } }

        public int ShowOrder { get; private set; }

        public class Record : INotifyPropertyChanged
        {
            public string Name
            {
                get
                {
                    if (_file == null)
                    {
                        return "==NEW==";
                    }
                    else
                    {
                        var name = Path.GetFileNameWithoutExtension(_file);
                        return IsDirty ? $"*{name}*" : name;
                    }
                }
            }

            private string _file;
            public string File { get { return _file; } set { _file = value; OnPropertyChanged(new PropertyChangedEventArgs("File")); } }
            public DData Data { get; set; }

            private bool _isDirty;
            public bool IsDirty
            {
                get
                {
                    return _isDirty;
                }
                set
                {

                    _isDirty = value;
                    OnPropertyChanged(new PropertyChangedEventArgs("Name"));

                }
            }

            public string Index { get; private set; }
            public string IndexFieldValue
            {
                get
                {
                    return DataBean.GetFieldByname(Index).Content2String();
                }
                set
                {
                    var f = DataBean.GetFieldByname(Index);
                    if (f is DInt fint)
                    {
                        fint.Value = int.Parse(value);
                    }
                    else if (f is DLong flong)
                    {
                        flong.Value = long.Parse(value);
                    }
                }
            }

            DBean DataBean { get { return (Data is DDynamicBean ? (Data as DDynamicBean).Value : Data) as DBean; } }

            public DData Origin { get; private set; }

            public Record(string file, DData data, string index)
            {
                this.File = file;
                this.Data = data;
                data.IsExpander = true;
                this.Origin = Data.Clone();
                this.Index = index;

                data.AnyChange += () => IsDirty = true;
            }

            public void Reset()
            {
                _isDirty = false;
                Data = Origin.Clone();
                Data.AnyChange += () => IsDirty = true;
                OnPropertyChanged(new PropertyChangedEventArgs("Name"));
                OnPropertyChanged(new PropertyChangedEventArgs("Data"));
            }

            public void Save()
            {
                IsDirty = false;
                Origin = Data.Clone();

                // save

                using (var writer = XmlWriter.Create(File, new XmlWriterSettings() { Indent = true, IndentChars = "\t" }))
                    WriteToXml(writer);
            }

            private void WriteToXml(XmlWriter writter)
            {
                writter.WriteStartDocument();

                writter.WriteStartElement("data");

                Data.WriteXml(writter);

                writter.WriteEndElement();

                writter.WriteEndDocument();
            }

            public event PropertyChangedEventHandler PropertyChanged;

            protected void OnPropertyChanged(PropertyChangedEventArgs e)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, e);
            }
        }

        internal void ForceSaveAll()
        {
            foreach (var r in Records)
            {
                ConfigControl.CheckSave(this, r);
            }
        }

        internal void SaveAll()
        {
            foreach (var r in Records)
            {
                if (r.IsDirty)
                    ConfigControl.CheckSave(this, r);
            }
        }

        public ObservableCollection<Record> Records { get; private set; }

        public Config(Defines ds, Bean bean, string csvDir, XmlElement node)
        {
            this.Defines = ds;
            CsvDir = csvDir;

            _bean = bean;
            SimpleName = node.GetAttribute("name").ToLower();

            var title = node.GetAttribute("title");
            Title = string.IsNullOrWhiteSpace(title) ? SimpleName : title;

            _index = node.GetAttribute("index");
            if (string.IsNullOrEmpty(_index))
            {
                _index = bean.HierarchyFields[0].Name;
            }
            else
            {
                if (bean.GetFieldByName(_index) == null)
                {
                    Err($"index:{_index} not {bean.FullName}'s field");
                }
            }

            var order = node.GetAttribute("show_order");
            if (!string.IsNullOrEmpty(order))
            {
                ShowOrder = int.Parse(order);
            }

            Records = new ObservableCollection<Record>();

            var es = node.GetAttribute("editor");
            IsEditorConfig = es == "true" || es == "1";

            var input = node.GetAttribute("input");
            if (string.IsNullOrWhiteSpace(input))
                Err("input can't be empty");
            _inputs = input.Split(';', ':', ',');
            if (IsEditorConfig)
            {
                foreach (var dir in _inputs)
                {
                    if (!Directory.Exists(Path.Combine(csvDir, dir)))
                        Err($"editor config:{bean.FullName} input:{input} directory not exists");
                }
            }
        }

        private void Err(string msg)
        {
            throw new Exception($"config:{_bean.FullName} {msg}");
        }


        public void ResolveDefine()
        {
            ValueType = new TBean(_bean, new Dictionary<string, string>());
            KeyType = ValueType.BeanType.GetFieldByName(_index).FType;
        }

        internal bool Load()
        {
            Log.Debug("== config:{0} load", _bean.FullName);
            if (IsEditorConfig)
            {
                var records = new List<Record>();
                foreach (var dir in _inputs)
                {
                    foreach (string file in Directory.GetFiles(Path.Combine(CsvDir, dir), "*.xml", SearchOption.AllDirectories))
                    {
                        Log.Debug(" == load data file: {0}", file);
                        try
                        {
                            var xml = new XmlDocument();
                            xml.Load(file);
                            var record = new Record(file, ValueType.New(xml.DocumentElement), _index);
                            records.Add(record);
                        }
                        catch (Exception e)
                        {
                            MessageBox.Show($"{file} 加载失败, 请先check配置正确 \n {e.Message} \n {e.StackTrace}");
                            return false;
                        }
                    }
                }
                records.Sort((a, b) => a.Name.CompareTo(b.Name));

                foreach (var r in records)
                {
                    Records.Add(r);
                }
            }
            else
            {
                Log.Debug("~~~~~~~~~~ normal load {0}", _bean.FullName);
            }
            return true;
        }

        public void ResolveData()
        {
            Log.Debug("== config:{0} resolve", _bean.FullName);
        }
    }
}
